#pragma once
#include "common.h"
#include "counter.h"
#include "config.h"

namespace config
{
    extern Switch ras_update_in_backend;
} // namespace config


enum class BranchType
{
    NONE,
    Branch,
    Jump,
    Call,
    Ret,
    CallRet
};

typedef struct
{
    word_t pc;
    BranchType type;
    word_t target;
    uint8_t sc; /* 2-bit saturation counter */
    bool valid;
} BTB_Entry;

typedef struct
{
    word_t target;
} RAS_Entry;

typedef struct
{
    RAS_Entry entry;
    uint32_t pointer;
} RAS_Res;

typedef struct
{
    bool valid;
    bool taken;
    word_t target;
    BranchType type;

    uint8_t btb_sc;
    bool ras_valid;
    uint32_t ras_pointer;
    RAS_Entry ras_entry;
} BPU_Res;

class RAS
{
public:
    std::vector<RAS_Entry> ras;
    size_t ras_size;
    size_t ras_depth;
    Counter ras_max_depth;
    uint32_t pointer;
    RAS(size_t ras_size);
    ~RAS();
    void reset();
    RAS_Res pop();
    RAS_Res push(RAS_Entry ras_entry);
    void update(word_t addr, BPU_Res correct_res);
};

class BPU
{
public:
    RAS ras;
    std::vector<BTB_Entry> btb;

    size_t btb_size, ras_size;

    BPU(size_t btb_size, size_t ras_size);
    ~BPU();
    void reset();
    BPU_Res predict(word_t addr);
    /* Call update() only on branch predict miss. */
    void update(word_t addr, BPU_Res correct_res);
};
